package game.logic.app;

import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PuzzleGrid extends JPanel
{
    private static final int GRID_SIZE = PuzzleBrick.UNIT_SIZE;

    private final boolean[] occupiedGrids = new boolean[64];
    private final List<Placement> placements = new ArrayList<>();
    private final List<Placement> barriers = new ArrayList<>();

    private record Placement(PuzzleBrick brick, int row, int col)
    {
    }

    public PuzzleGrid()
    {
        setBackground(Color.decode("#3d3d3d"));

        setLayout(null);
        Dimension size = new Dimension(GRID_SIZE * 8, GRID_SIZE * 8);
        setPreferredSize(size);
        setMaximumSize(size);
        setMinimumSize(size);
        setBorder(new EmptyBorder(5, 5, 5, 5));
    }

    public int getBarriersCount()
    {
        return barriers.size();
    }

    public String getPuzzleIdentity()
    {
        if (barriers.size() != 3)
            return ""; // 障碍没有摆满，无法确定题目身份。

        char[] identity = new char[64];
        Arrays.fill(identity, '_');
        for (Placement barrier : barriers)
        {
            int startRow = barrier.row;
            int startCol = barrier.col;
            int endRow = startRow + barrier.brick.getYUnits();
            int endCol = startCol + barrier.brick.getXUnits();
            for (int r = startRow; r < endRow; r++)
                for (int c = startCol; c < endCol; c++)
                    identity[r * 8 + c] = barrier.brick.getId();
        }
        return new String(identity);
    }

    public void init(String puzzle)
    {
        placements.clear();
        barriers.clear();
        removeAll();

        updateGridOccupations();

        List<PuzzleBrick> bricks = PuzzleBricks.barriers();
        for (PuzzleBrick brick : bricks)
            placeBySolution(puzzle, brick);
    }

    public void solve(String solution)
    {
        placements.clear();
        barriers.clear();
        removeAll();

        List<PuzzleBrick> all = PuzzleBricks.all();
        for (PuzzleBrick brick : all)
            placeBySolution(solution, brick);
    }

    public void reset()
    {
        placements.clear();
        barriers.clear();
        Arrays.fill(occupiedGrids, false);
        removeAll();
    }

    public void resetPuzzle()
    {
        // 仅保留障碍，清除其它
        List<Placement> toBeRemoved = placements.stream()
                                                .filter(placement -> !placement.brick.isBarrier())
                                                .toList();

        if (toBeRemoved.isEmpty())
            return;

        placements.removeAll(toBeRemoved);
        updateGridOccupations();

        for (Placement placement : toBeRemoved)
        {
            remove(placement.brick);
        }
    }

    public boolean isSolved()
    {
        updateGridOccupations();
        for (boolean gridOccupied : occupiedGrids)
            if (!gridOccupied)
                return false;
        return true;
    }

    public boolean placeBrickAt(PuzzleBrick brick, Point p)
    {
        if (canPlaceHere(brick, p))
        {
            int startRow = p.y / GRID_SIZE;
            int startCol = p.x / GRID_SIZE;

            Placement placement = new Placement(brick, startRow, startCol);
            placements.add(placement);
            if (brick.isBarrier())
                barriers.add(placement);

            add(brick);
            brick.setBounds(startCol * GRID_SIZE, startRow * GRID_SIZE, brick.getWidth(), brick.getHeight());

            return true;
        }
        return false;
    }

    public void removeBrick(PuzzleBrick brick)
    {
        placements.stream()
                  .filter(p -> p.brick == brick)
                  .findAny()
                  .ifPresent(placement -> {
                      placements.remove(placement);
                      barriers.remove(placement);
                      updateGridOccupations();
                      remove(brick);
                  });
    }

    public void drawPossibleLocation(PuzzleBrick brick, Point p)
    {
        if (contains(p) && canPlaceHere(brick, p))
        {
            int startRow = p.y / GRID_SIZE;
            int startCol = p.x / GRID_SIZE;
            int endRow = startRow + brick.getYUnits();
            int endCol = startCol + brick.getXUnits();
            for (int r = startRow; r < endRow; r++)
                for (int c = startCol; c < endCol; c++)
                    occupiedGrids[r * 8 + c] = true;
            repaint();
        }
    }

    private void placeBySolution(String solution, PuzzleBrick brick)
    {
        char[] chars = solution.toCharArray();
        int startRow = 0, startCol = 0;
        for (int i = 0; i < chars.length; i++)
            if (chars[i] == brick.getId())
            {
                startRow = i / 8;
                startCol = i % 8;
                break;
            }

        int endRow = startRow + brick.getYUnits();
        int endCol = startCol + brick.getXUnits();
        if (endRow > 8 || endCol > 8 || chars[(endRow - 1) * 8 + (endCol - 1)] != brick.getId())
            brick.rotate();

        Placement placement = new Placement(brick, startRow, startCol);
        placements.add(placement);
        if (brick.isBarrier())
            barriers.add(placement);

        add(brick);
        brick.setBounds(startCol * GRID_SIZE, startRow * GRID_SIZE, brick.getWidth(), brick.getHeight());
    }

    private boolean canPlaceHere(PuzzleBrick brick, Point p)
    {
        updateGridOccupations();

        // p是brick左上角小方块的中心点在grid内的坐标
        int startRow = p.y / GRID_SIZE;
        int startCol = p.x / GRID_SIZE;
        int endRow = startRow + brick.getYUnits();
        int endCol = startCol + brick.getXUnits();

        if (endRow > 8 || endCol > 8)
            return false;

        for (int r = startRow; r < endRow; r++)
            for (int c = startCol; c < endCol; c++)
                if (occupiedGrids[r * 8 + c])
                    return false; // 已被占用
        return true;
    }

    private void updateGridOccupations()
    {
        Arrays.fill(occupiedGrids, false);
        for (Placement placement : placements)
        {
            int startRow = placement.row;
            int startCol = placement.col;
            int endRow = startRow + placement.brick.getYUnits();
            int endCol = startCol + placement.brick.getXUnits();
            for (int r = startRow; r < endRow; r++)
                for (int c = startCol; c < endCol; c++)
                    occupiedGrids[r * 8 + c] = true;
        }
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        Dimension size = getPreferredSize();
        int width = size.width;
        int height = size.height;

        g2d.setStroke(new BasicStroke(2));
        g2d.setPaint(UIManager.getColor("Panel.background"));
        for (int i = 1; i < 8; i++)
        {
            g2d.drawLine(GRID_SIZE * i, 0, GRID_SIZE * i, height);
            g2d.drawLine(0, GRID_SIZE * i, width, GRID_SIZE * i);
        }

        g2d.setStroke(new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER));
        int d = 42; // 圆环直径

        g2d.setColor(Color.decode("#4a4a4a"));
        for (int r = 0; r < 8; r++)
        {
            for (int c = 0; c < 8; c++)
            {
                int cx = c * GRID_SIZE + (GRID_SIZE - d) / 2;
                int cy = r * GRID_SIZE + (GRID_SIZE - d) / 2;
                g2d.drawRoundRect(cx, cy, d, d, 10, 10);
            }
        }

        g2d.setColor(Color.decode("#202020"));
        for (int r = 0; r < 8; r++)
        {
            for (int c = 0; c < 8; c++)
            {
                int cx = c * GRID_SIZE + (GRID_SIZE - d) / 2;
                int cy = r * GRID_SIZE + (GRID_SIZE - d) / 2;

                if (occupiedGrids[r * 8 + c])
                    g2d.fillRoundRect(cx, cy, d, d, 10, 10);
            }
        }

        g2d.dispose();
    }
}
